/*
 * Chastoslov
 * Copyright (C) 2009 Alexandr Stroghov
 * 
 * stroghov@gmail.com
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms of the 
 * GNU General Public License, version 2, as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program; if 
 * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
 * MA 02111-1307 USA
*/

// Модель данных таблицы, работающая с запросами к базам данных 

package world.ah.wf.guiclient;

import javax.swing.table.AbstractTableModel;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;

public class DBTableModel extends AbstractTableModel { 
  private boolean editable;
  // Список определенных колонок 
  private DBField[] dBFields;
  public void setDBFields(DBField [] dBFields) {this.dBFields = dBFields;}
  public DBField[] getDBFields() {return dBFields;}
  // Количество полей в неборе 
  private int realFieldCount;
  // здесь мы будем хранить столбцы 
  private ArrayList<DBField> columns = new ArrayList<DBField>(); 
  // список типов столбцов
  //private ArrayList columnTypes = new ArrayList(); 
  // хранилище для полученных данных из базы данных
  private ArrayList<ArrayList<Object>> data = new ArrayList<ArrayList<Object>>();
  // конструктор позволяет задать возможность редактирования
  public DBTableModel(boolean editable) {this.editable = editable;}
  // количество строк 
  public int getRowCount() {
    synchronized (data) {return data.size();} 
  }
  // количество столбцов 
  public int getColumnCount() {return columns.size();}
  // тип данных столбца 
  public Class getColumnClass(int column) {
    return (/*(DBField)*/columns.get(column)).getType();
  }
  // название столбца
  public String getColumnName(int column) {
    return (/*(DBField)*/columns.get(column)).getTitle();
  }
  // данные в ячейке
  public Object getValueAt(int row, int column) {
    synchronized (data) {
      ArrayList<Object> r = /*(ArrayList)*/data.get(row); // строка 
      int c = (/*(DBField)*/columns.get(column)).getMappingIndex(); // индекс колонки в хранилище 
      return convertToShow(column, r.get(c));
    }
  }
  // возможность редактирования 
  public boolean isCellEditable(int row, int column) {
    return this.editable && !(/*(DBField)*/columns.get(column)).getReadOnly();
  }
  // замена значения ячейки
  public void setValueAt(Object value, int row, int column) {
    synchronized (data) {
      ArrayList<Object> r = /*(ArrayList<Object>)*/data.get(row); // строка 
      int c = (/*(DBField)*/columns.get(column)).getMappingIndex(); // индекс колонки в хранилище 
      DBPostResult res = postChanges(column, r.toArray(), value);
      if(res.isSucceed()) {
        Object[] nr = res.getNewRecord();
        r.set(c, convertToSave(column, value));
        if(nr != null) {
          boolean wasChanged = false;
          for(int i = 0; i < nr.length; i++) {
            if(nr[i] != null) r.set(i, nr[i]);
            wasChanged = true;
          }
          if(wasChanged) this.fireTableRowsUpdated(row, row);
        }
      }
    }
  }
  // получение данных из объекта ResultSet 
  public void setDataSource(ResultSet rs) throws Exception { 
    //удаляем прежние данные 
    data.clear();
    columns.clear();
    realFieldCount = 0;
    if(rs == null) fireTableStructureChanged();
    else {
      boolean special = dBFields != null && dBFields.length > 0;
      // получаем вспомогательную информацию о столбцах
      ResultSetMetaData rsmd = rs.getMetaData(); 
      realFieldCount = rsmd.getColumnCount();  
      // Добавляем определения колонок 
      if(special) // если набор был задан
        for (int i = 0; i < dBFields.length; i++) { 
          DBField f = (DBField)(dBFields[i].clone());
          columns.add(f);
          f.setMappingIndex(-1);
          for(int j = 1; j <= realFieldCount; j++)
            if(rsmd.getColumnName(j).equalsIgnoreCase(f.getName())) {
              f.setMappingIndex(j - 1);
              if(f.getTitle() == "") f.setTitle(rsmd.getColumnName(j));
              break;
            }
        } 
      else // если набор НЕ был задан
        for (int i = 1; i <= realFieldCount; i++) { 
          DBField f = new DBField(rsmd.getColumnName(i), 
                                  rsmd.getColumnName(i), 
                                  Class.forName(rsmd.getColumnClassName(i)),
                                  false
                                 );
          f.setMappingIndex(i - 1);
          columns.add(f);
        }
      // сообщаем об изменениях в структуре данных
      fireTableStructureChanged();
      // получаем данные
      while (rs.next()) { 
        // здесь будем хранить ячейки одной строки 
        ArrayList<Object> row = new ArrayList<Object>(); 
          for (int i = 1; i <= realFieldCount; i++) {
            if (Class.forName(rsmd.getColumnClassName(i)) == String.class)
              row.add(rs.getString(i));
            else
              row.add(rs.getObject(i));
          }
        synchronized (data) {data.add(row);}
        // сообщаем о прибавлении строки
        fireTableRowsInserted(data.size() - 1, data.size() - 1);
      }
    }
  }
  // Добавление данных // НАБРОСОК
  public void insert(Object value) throws Exception {}
  public void insert() {
    ArrayList<Object> row = new ArrayList<Object>(); 
    for(int i = 0; i < realFieldCount; i++) 
      row.add(null);
    synchronized (data) {data.add(row);}
        fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
  }
  // Удаление данных // НАБРОСОК
  public void delete(int row) {
    ArrayList<Object> r = /*(ArrayList)*/data.get(row); // строка 
    if(postDeletion(r.toArray())) {
      synchronized (data) {data.remove(row);}
      fireTableRowsDeleted(row, row);
    }
  }
  // Преобразует объект для показа в сетке 
  public Object convertToShow(int column, Object value) {return value;}
  // Преобразует объект для сохранения
  public Object convertToSave(int column, Object value) {return value;}
  // Post changes 
  public DBPostResult postChanges(int column, Object [] record, Object newValue) {
    return new DBPostResult(true, null);
  }
  // Post deletion 
  public boolean postDeletion(Object [] record) {return true;}
}

